之前通过读取/proc/pid/mem的方法读取某个进程的内存数据,mem内部是用copy_from_user实现的,是对虚拟地址进行的操作。但是在某一时刻,该进程的所有内存页不一定都已经被加载到内存。由于虚拟内存的存在,只有那页代码被访问到时(copy_from_user()会判断缺页的情况),才会产生缺页中断,将该页代码加载到内存。这种方式并不够理想,理想的方法是判断哪些数据页已加载到内存中,然后对其进行度量。
在google一番后,发现有三个小程序涉及到的知识可以完成这一任务,第一个是dram.c,用来创建字符设备,这个字符设备将物理内存虚拟为一个dev/dram文件。第二个是fileview.cpp,用来读取dram文件,从而获取物理内存页的数据。第三个是translate.c,用来将虚拟地址转换为物理地址。
这样编写一个内核模块,就可以实现对进程代码段的分页度量了。以下是三个小程序的使用方法、代码注释、内核模块。
使用方法
1 | cd Access_Physical_Memory |
读取物理内存
字符设备dram.c。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163//-------------------------------------------------------------------
// dram.c
//
// This module implements a Linux character-mode device-driver
// for the processor's installed physical memory. It utilizes
// the kernel's 'kmap()' function, as a uniform way to provide
// access to all the memory-zones (including the "high memory"
// on systems with more than 896MB of installed physical ram).
// The access here is 'read-only' because we deem it too risky
// to the stable functioning of our system to allow every user
// the unrestricted ability to arbitrarily modify memory-areas
// which might contain some "critical" kernel data-structures.
// We implement an 'llseek()' method so that users can readily
// find out how much physical processor-memory is installed.
//
// NOTE: Developed and tested with Linux kernel version 2.6.10
//
// programmer: ALLAN CRUSE
// written on: 30 JAN 2005
// revised on: 28 JAN 2008 -- for Linux kernel version 2.6.22.5
// revised on: 06 FEB 2008 -- for machines having 4GB of memory
//-------------------------------------------------------------------
char modname[] = "dram"; // for displaying driver's name
int my_major = 85; // note static major assignment
loff_t dram_size; // total bytes of system memory
/*
#ifdef __GNUC__
typedef long long __kernel_loff_t;
#endif
#if defined(__GNUC__)
typedef __kernel_loff_t loff_t;
#endif
loff_t 是一个long long类型
*/
loff_t my_llseek( struct file *file, loff_t offset, int whence );
ssize_t my_read( struct file *file, char *buf, size_t count, loff_t *pos );
//指定成员赋值,cpp不支持
//该结构体里都是函数指针,llseek用于改变文件的当前读写位置。read用于从设备获取数据。
struct file_operations
my_fops = {
owner: THIS_MODULE,
llseek: my_llseek,
read: my_read,
};
static int __init dram_init( void )
{
printk( "<1>\nInstalling \'%s\' module ", modname );
printk( "(major=%d)\n", my_major );
//get_num_physpages()获取 所有物理内存减去内核所保留内存块后的剩余内存(是内存地址数,不是内存页数)。
dram_size = (loff_t)get_num_physpages() << PAGE_SHIFT;
// %08llX 前补0,域宽8位,大写16进制输出
printk( "<1> ramtop=%08llX (%llu MB)\n", dram_size, dram_size >> 20 );
//register_chrdev注册字符设备,
return register_chrdev( my_major, modname, &my_fops );
}
static void __exit dram_exit( void )
{
//取消字符设备注册
unregister_chrdev( my_major, modname );
printk( "<1>Removing \'%s\' module\n", modname );
}
/*
file:为进行读取信息的目标文件,
buf:为对应放置信息的缓冲区(即用户空间内存地址);
count:为要读取的信息长度;
pos:为读的位置相对于文件开头的偏移,这里的pos是想读取的物理地址
*/
ssize_t my_read( struct file *file, char *buf, size_t count, loff_t *pos )
{
struct page *pp;
void *from;
int page_number, page_indent, more;
// we cannot read beyond the end-of-file
//如果读取位置超出物理内存尺寸,则退出
if ( *pos >= dram_size ) return 0;
// determine which physical page to temporarily map
// and how far into that page to begin reading from
//根据物理地址计算对应页号
page_number = *pos / PAGE_SIZE;
//计算页内偏移
page_indent = *pos % PAGE_SIZE;
// map the designated physical page into kernel space
//If kerel vesion is 2.6.32 or later, please use pfn_to_page() to get page, and include
// asm-generic/memory_model.h
//这里我的内核是3.16.82,所以改成if 1
//根据物理页号获取mem_map数组中相应地址
pp = pfn_to_page( page_number);
pp = &mem_map[ page_number ];
//kmap在永久内核映射区,创建高端页框(物理页)到内核地址空间(线性地址)的长期映射
from = kmap( pp ) + page_indent;
// cannot reliably read beyond the end of this mapped page
//每次只读取不超过一页的数据
if ( page_indent + count > PAGE_SIZE ) count = PAGE_SIZE - page_indent;
// now transfer count bytes from mapped page to user-supplied buffer
/*
unsigned long copy_to_user(void *to, const void *from, unsigned long n)
to:目标地址(用户空间)
from:源地址(内核空间)
n:将要拷贝数据的字节数
返回:成功返回0,失败返回没有拷贝成功的数据字节数
*/
more = copy_to_user( buf, from, count );
// ok now to discard the temporary page mapping
//删除之前的映射
kunmap( pp );
// an error occurred if less than count bytes got copied
if ( more ) return -EFAULT;
// otherwise advance file-pointer and report number of bytes read
//往后推进读取位置,返回读取的字节数
*pos += count;
return count;
}
/*
重新定位文件读写偏移量
whence有以下取值:
SEEK_SET 偏移量设置为offset字节。
SEEK_CUR 偏移量设置为当前位置加上offset字节。
SEEK_END 偏移量设置为文件大小加上偏移字节大小。
*/
loff_t my_llseek( struct file *file, loff_t offset, int whence )
{
loff_t newpos = -1;
switch( whence )
{
case 0: newpos = offset; break; // SEEK_SET
case 1: newpos = file->f_pos + offset; break; // SEEK_CUR
case 2: newpos = dram_size + offset; break; // SEEK_END
}
if (( newpos < 0 )||( newpos > dram_size )) return -EINVAL;
file->f_pos = newpos;
return newpos;
}
MODULE_LICENSE("GPL");
module_init( dram_init );
module_exit( dram_exit );fileview.cpp文件用来读取dev/dram字符设备,并输出内存页上的数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305//----------------------------------------------------------------
// fileview.cpp
//
// This program displays the contents of a specified file
// in hexadecimal and ascii formats (including any device
// special files representing storage media). A user may
// navigate the file's contents using arrow-key commands,
// or may adjust the format of the hexadecimal display to
// select from among five data-sizes: byte (B), word (W),
// doubleword (D), quadword (Q) or octaword (O). It also
// is possible to seek to a specified position within the
// file by hitting the <ENTER>-key and then typing in the
// desired (hexadecimal) address. Type <ESCAPE> to quit.
此程序以十六进制和ascii格式显示指定文件的内容(包括表示存储介质的任何设备专用文件)
用户可以使用箭头键命令浏览文件内容,也可以调整十六进制显示的格式,
以便从五种数据大小中进行选择:字节(B)、字(W)、双字(D)、四字(Q)或八字(O)。
也可以通过按<ENTER>键,然后键入所需的(十六进制)地址,在文件中查找到指定的位置。
键入<ESCAPE>退出。
// compile-and-link using: $ make fileview
//
// programmer: ALLAN CRUSE
// written on: 26 OCT 2002
// revised on: 07 JUN 2006 -- removed reliance on 'ncurses'
//----------------------------------------------------------------
//键盘按键
char progname[] = "FILEVIEW";
char filename[ MAXNAME + 1 ];
char buffer[ BUFSIZE ];
char outline[ 80 ];
// ./fileview /dev/dram
int main( int argc, char *argv[] )
{
// setup the filename (if supplied), else terminate
//此时artv[1]为dev/dram
if ( argc > 1 ) strncpy( filename, argv[1], MAXNAME );
else { fprintf( stderr, "argument needed\n" ); exit(1); }
// open the file for reading
//以只读模式打开/dev/dram字符设备
int fd = open( filename, O_RDONLY );
if ( fd < 0 ) { perror( filename ); exit(1); }
// obtain the filesize (if possible)
//lseek64用于大文件内的读写位置跳转(可以设置64位的地址),返回相对于文件首的偏移量
long long filesize = lseek64( fd, 0LL, SEEK_END );
if ( filesize < 0LL )
{
fprintf( stderr, "cannot locate \'end-of-file\' \n" );
exit(1);
}
long long incmin = ( 1LL << 8 );
long long incmax = ( 1LL << 36 );
long long posmin = 0LL;
long long posmax = (filesize - 241LL)&~0xF;
if ( posmax < posmin ) posmax = posmin;
// initiate noncanonical terminal input
struct termios tty_orig;
//获取终端相关参数,第一个参数是fd,
tcgetattr( STDIN_FILENO, &tty_orig );
struct termios tty_work = tty_orig;
//关闭终端回显和规范模式(规范模式是什么?)
tty_work.c_lflag &= ~( ECHO | ICANON ); // | ISIG );
tty_work.c_cc[ VMIN ] = 1;
tty_work.c_cc[ VTIME ] = 0;
//设置终端的相关参数
tcsetattr( STDIN_FILENO, TCSAFLUSH, &tty_work );
printf( "\e[H\e[J" );
// display the legend
int i, j, k;
k = (77 - strlen( progname ))/2;
//在1行k列位置打印FILEVIEW
printf( "\e[%d;%dH %s ", 1, k, progname );
k = (77 - strlen( filename ))/2;
//打印/dev/dram
printf( "\e[%d;%dH\'%s\'", 3, k, filename );
char infomsg[ 80 ];
sprintf( infomsg, "filesize: %llu (=0x%013llX)", filesize, filesize );
k = (78 - strlen( infomsg ));
printf( "\e[%d;%dH%s", 24, k, infomsg );
fflush( stdout );
// main loop to navigate the file
long long pageincr = incmin;
long long lineincr = 16LL;
long long position = 0LL;
long long location = 0LL;
int format = 1;
int done = 0;
while ( !done ){
// erase prior buffer contents
//清除缓冲区内容,此缓冲区用来临时储存物理内存数据
for (j = 0; j < BUFSIZE; j++) buffer[ j ] = ~0;
// restore 'pageincr' to prescribed bounds
if ( pageincr == 0LL ) pageincr = incmax;
else if ( pageincr < incmin ) pageincr = incmin;
else if ( pageincr > incmax ) pageincr = incmax;
// get current location of file-pointer position
//将读写位置设置为0,并获取当前读写指针的位置
location = lseek64( fd, position, SEEK_SET );
// try to fill 'buffer[]' with data from the file
char *where = buffer;
int to_read = BUFSIZE;
//读取物理内存数据到buffer数组中
while ( to_read > 0 ){
int nbytes = read( fd, where, to_read );
if ( nbytes <= 0 ) break;
to_read -= nbytes;
where += nbytes;
}
int datalen = BUFSIZE - to_read;
// display the data just read into the 'buffer[]' array
unsigned char *bp;
unsigned short *wp;
unsigned int *dp;
unsigned long long *qp;
for (i = 0; i < BUFHIGH; i++){
int linelen;
// draw the line-location (13-digit hexadecimal)
//第一列打印地址到outline
linelen = sprintf( outline, "%013llX ", location );
// draw the line in the selected hexadecimal format
switch ( format ){
//以字节为单位读取buffer,然后用大写16进制输出到outline。
case 1: // 'byte' format
bp = (unsigned char*)&buffer[ i*BUFWIDE ];
for (j = 0; j < BUFWIDE; j++)
linelen += sprintf( outline+linelen,
"%02X ", bp[j] );
break;
case 2: // 'word' format
wp = (unsigned short*)&buffer[ i*BUFWIDE ];
for (j = 0; j < BUFWIDE/2; j++)
linelen += sprintf( outline+linelen,
" %04X ", wp[j] );
break;
case 4: // 'dword' format
dp = (unsigned int*)&buffer[ i*BUFWIDE ];
for (j = 0; j < BUFWIDE/4; j++)
linelen += sprintf( outline+linelen,
" %08X ", dp[j] );
break;
case 8: // 'qword' format
qp = (unsigned long long*)&buffer[ i*BUFWIDE ];
for (j = 0; j < BUFWIDE/8; j++)
linelen += sprintf( outline+linelen,
" %016llX ", qp[j] );
break;
case 16: // 'octaword'
qp = (unsigned long long*)&buffer[ i*BUFWIDE ];
linelen += sprintf( outline+linelen, " " );
linelen += sprintf( outline+linelen,
" %016llX%016llX ", qp[1], qp[0] );
linelen += sprintf( outline+linelen, " " );
break;
}
// draw the line in ascii format
//以ascii格式输出数据到outline
for (j = 0; j < BUFWIDE; j++){
char ch = buffer[ i*BUFWIDE + j ];
if (( ch < 0x20 )||( ch > 0x7E )) ch = '.';
linelen += sprintf( outline+linelen, "%c", ch);
}
// transfer this output-line to the screen
//打印outline
printf( "\e[%d;%dH%s", ROW+i, COL, outline );
// advance 'location' for the next output-line
location += BUFWIDE;
}
printf( "\e[%d;%dH", 23, COL );
fflush( stdout );
// await keypress
long long inch = 0LL;
read( STDIN_FILENO, &inch, sizeof( inch ) );
printf( "\e[%d;%dH%60s", 23, COL, " " );
// interpret navigation or formatting command
//读取输入的字符
inch &= 0x00FFFFFFLL;
switch ( inch ){
// move to the file's beginning/ending
//移到文件首
case 'H': case 'h':
case KB_HOME: position = posmin; break;
case 'E': case 'e':
case KB_END: position = posmax; break;
// move forward/backward by one line
case KB_LNDN: position += BUFWIDE; break;
case KB_LNUP: position -= BUFWIDE; break;
// move forward/packward by one page
case KB_PGDN: position += pageincr; break;
case KB_PGUP: position -= pageincr; break;
// increase/decrease the page-size increment
case KB_RGHT: pageincr >>= 4; break;
case KB_LEFT: pageincr <<= 4; break;
// reset the hexadecimal output-format
case 'B': case 'b': format = 1; break;
case 'W': case 'w': format = 2; break;
case 'D': case 'd': format = 4; break;
case 'Q': case 'q': format = 8; break;
case 'O': case 'o': format = 16; break;
// seek to a user-specified file-position
case KB_SEEK:
printf( "\e[%d;%dHAddress: ", 23, COL );
fflush( stdout );
{
char inbuf[ 16 ] = {0};
//tcsetattr( STDIN_FILENO, TCSANOW, &tty_orig );
int i = 0;
while ( i < 15 ){
long long ch = 0;
read( STDIN_FILENO, &ch, sizeof( ch ) );
ch &= 0xFFFFFF;
if ( ch == '\n' ) break;
if ( ch == KB_QUIT ) { inbuf[0] = 0; break; }
if ( ch == KB_LEFT ) ch = KB_BACK;
if ( ch == KB_DEL ) ch = KB_BACK;
if (( ch == KB_BACK )&&( i > 0 ))
{
inbuf[--i] = 0;
printf( "\b \b" );
fflush( stdout );
}
if (( ch < 0x20 )||( ch > 0x7E )) continue;
inbuf[ i++ ] = ch;
printf( "%c", ch );
fflush( stdout );
}
printf( "\e[%d;%dH%70s", 23, COL, " " );
fflush( stdout );
position = strtoull( inbuf, NULL, 16 );
position &= ~0xFLL; // paragraph align
}
break;
// program termination
case KB_QUIT: done = 1; break;
default:
printf( "\e[%d;%dHHit <ESC> to quit", 23, 2 );
}
fflush( stdout );
// insure that 'position' remains within bounds
if ( position < posmin ) position = posmin;
if ( position > posmax ) position = posmax;
}
// restore canonical terminal behavior
//复原终端的各项参数
tcsetattr( STDIN_FILENO, TCSAFLUSH, &tty_orig );
printf( "\e[%d;%dH\e[0J\n", 23, 0 );
}Makefile文件
1
2
3
4
5
6
7
8
9
10
11
12#Makefile
ifneq ($(KERNELRELEASE),)
obj-m := dram.o
else
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
rm -r -f .tmp_versions *.mod.c .*.cmd *.o *.symvers
endif
虚拟地址转换为物理地址
由于虚拟内存的存在,一个二进制文件不是整个代码段加载到内存的。一个进程的内存页是否加载到物理内存,系统是有记录的。/proc/$pid/pagemap文件就记录了pid进程的虚拟地址和物理地址的映射情况。
- translate.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
const int __endian_bit = 1;
int i, c, pid, status;
unsigned long virt_addr;
uint64_t read_val, file_offset, page_size;
char path_buf [0x100] = {};
FILE * f;
char *end;
int read_pagemap(char * path_buf, unsigned long virt_addr);
int main(int argc, char ** argv){
if(argc!=3){
printf("Argument number is not correct!\n pagemap PID VIRTUAL_ADDRESS\n");
return -1;
}
if(!memcmp(argv[1],"self",sizeof("self"))){
sprintf(path_buf, "/proc/self/pagemap");
pid = -1;
}
else{
/*从字符串中解析整数
argv[1]中为字符串
end是一个字符指针,函数解析完long整数后,会将end指向整数之后的第一个字符。end如果为null,则不设置。
10表示按照10进制解析数据,如果是0表示按照其本身进制标记来解析。
*/
pid = strtol(argv[1],&end, 10);
if (end == argv[1] || *end != '\0' || pid<=0){
printf("PID must be a positive number or 'self'\n");
return -1;
}
}
//strtoll: Convert string to long long integer
virt_addr = strtoll(argv[2], NULL, 16);
if(pid!=-1)
sprintf(path_buf, "/proc/%u/pagemap", pid);
//获得一页内存大小
page_size = getpagesize();
read_pagemap(path_buf, virt_addr);
return 0;
}
/*
path_buf: /proc/pid/pagemap
virt_addr:虚拟地址
*/
int read_pagemap(char * path_buf, unsigned long virt_addr){
printf("Big endian? %d\n", is_bigendian());
//文件访问模式 r表示读,b表示以二进制读取
f = fopen(path_buf, "rb");
if(!f){
printf("Error! Cannot open %s\n", path_buf);
return -1;
}
//Shifting by virt-addr-offset number of bytes
//and multiplying by the size of an address (the size of an entry in pagemap file)
//pagemap中有一个个的页映射实体,每一个实体占8个字节。即64位,每位的作用见下一节的说明。
file_offset = virt_addr / page_size * PAGEMAP_ENTRY;
printf("Vaddr: 0x%lx, Page_size: %lld, Entry_size: %d\n", virt_addr, page_size, PAGEMAP_ENTRY);
printf("Reading %s at 0x%llx\n", path_buf, (unsigned long long) file_offset);
//设置文件指针f的偏移,偏移值以SEEK_SET为基准,偏移file_offset个字节。如果成功,返回0.否则返回非0值并设置error
status = fseek(f, file_offset, SEEK_SET);
if(status){
perror("Failed to do fseek!");
return -1;
}
errno = 0;
read_val = 0;
unsigned char c_buf[PAGEMAP_ENTRY];
//以字节为单位读取PAGEMAP_ENTRY到c_buf中
for(i=0; i < PAGEMAP_ENTRY; i++){
c = getc(f);
if(c==EOF){
printf("\nReached end of the file\n");
return 0;
}
if(is_bigendian())
c_buf[i] = c;
else
c_buf[PAGEMAP_ENTRY - i - 1] = c;
printf("[%d]0x%x ", i, c);
}
//将c_buf的数据转存到uint64_t型变量read_val中
for(i=0; i < PAGEMAP_ENTRY; i++){
//printf("%d ",c_buf[i]);
read_val = (read_val << 8) + c_buf[i];
}
printf("\n");
printf("Result: 0x%llx\n", (unsigned long long) read_val);
//第64位为1表示该虚拟地址已经分配了相应的物理页,然后输出了物理地址
if(GET_BIT(read_val, 63)) {
uint64_t pfn = GET_PFN(read_val);
printf("PFN: 0x%llx (0x%llx)\n", pfn, pfn * page_size + virt_addr % page_size);
} else
printf("Page not present\n");
if(GET_BIT(read_val, 62))
printf("Page swapped\n");
fclose(f);
return 0;
}
分页度量进程的代码段
基于以上几个知识点,可以实现在某一时刻分页度量当时已被加载到物理内存的数据。
内核模块代码:https://github.com/TWS-YIFEI/Dynamic_measurement_of_process_integrity
该模块实现了度量函数,度量的动作可以通过截获系统调用来触发。
知识点记录
page_to_pfn
1
2
3
4
5
6
7
8
9
page是mem_map_t类型的指针。(mem_map结构体数组用来表示所有物理页)
pfn(page frame number),是物理页号 (还是物理地址?为什么这里要加上PHYS_PFN_OFFSET,page-mem_map不就是页号了吗)
page-mem_map表示该页在mem_map数组中的偏移个数,就像:
int a[100];
a[10]-a; //a[10]就好比page,a就好比mem_map,只不过mem_map是一个结构体数组
变换一种形式可以更容易理解:(mem_map + (pfn - PHYS_PFN_OFFSET))判断存储方式是大端还是小端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
291.利用联合体
union{
short s; //占两个字节
char c[sizeof(short)];
}un;
un.s = 0x0102;
if (sizeof(short) == 2) {
//un.c[0]的地址为低位,如果存储的数据为高位数据,则为大端
if (un.c[0] == 1 && un.c[1] == 2)
printf("big-endian\n");
else if (un.c[0] == 2 && un.c[1] == 1)
printf("little-endian\n");
else
printf("unknown\n");
} else
printf("sizeof(short) = %d\n", sizeof(short));
2.使用强制类型转换
int is_little_endian(void){
unsigned short flag=0x4321;
if (*(unsigned char*)&flag==0x21)
return 1;
else
return 0;
}
3.强制类型转换
const int __endian_bit = 1;pagemap中每一个实体的结构
1
2
3
4
5
6
7Bits 0-54: page frame number(PFN) if present
Bits 0-4: swap type if swapped
Bits 5-54: swap offset if swapped
Bits 55-60:page shift
Bit 61: reserved ofr future use
Bit 62: page swapped
Bit 63: page present虚拟内存&物理内存
问题记录
- 在编译执行make编译dram模块时遇到下列信息,可以尝试这个方法:https://blog.csdn.net/u012343297/article/details/79141878 。
1
2
3
4[root@localhost Access_Physical_Memory]# make
make -C /lib/modules/3.10.0-957.el7.x86_64/build SUBDIRS=/root/Access_Physical_Memory modules
make: *** /lib/modules/3.10.0-957.el7.x86_64/build: No such file or directory. Stop.
make: *** [default] Error 2